/**

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     licenses@blazegraph.com

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
 * Created on Aug 16, 2010
 */

package com.bigdata.bop;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

/**
 * Abstract base class for copy-on-write {@link BOp}s. The {@link BOpBase} class
 * is used for query evaluation operators. The copy-on-write contract provides a
 * safety margin during concurrent evaluation of query plans by ensuring that
 * all references are fully published.
 * <p>
 * Instances of this class are effectively immutable (mutation APIs always
 * return a deep copy of the operator to which the mutation has been applied),
 * {@link Serializable} to facilitate distributed computing, and
 * {@link Cloneable} to facilitate non-destructive tree rewrites.
 * <p>
 * <h2>Constructor patterns</h2>
 * <p>
 * {@link BOp}s should define the following public constructors
 * <dl>
 * <dt>
 * <code>public <i>Class</i>(BOp[] args, Map&lt;String,Object&gt; anns)</code></dt>
 * <dd>A shallow copy constructor. This is used when initializing a {@link BOp}
 * from the caller's data or when generated a query plan from Prolog. There are
 * some exceptions to this rule. For example, {@link Constant} does not define a
 * shallow copy constructor because that would not provide a means to set the
 * constant's value.</dd>
 * <dt><code>public <i>Class</i>(<i>Class</i> src)</code></dt>
 * <dd>A deep copy constructor. Mutation methods make a deep copy of the
 * {@link BOp}, apply the mutation to the copy, and then return the copy. This
 * is the "effectively immutable" contract. Again, there are some exceptions.
 * For example, {@link Var} provides a canonicalized mapping such that reference
 * tests may be used to determine if two {@link Var}s are the same. In order to
 * support that contract it overrides {@link Var#clone()}.</dd>
 * </dl>
 * 
 * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
 * @version $Id$
 */
public class BOpBase extends CoreBaseBOp {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    /**
	 * The argument values - <strong>direct access to this field is
	 * discouraged</strong> - the field is protected to support
	 * <em>mutation</em> APIs and should not be relied on for other purposes.
	 * <p>
	 * Note: This field is reported out as a {@link List} so we can make it
	 * thread safe and, if desired, immutable. However, it is internally a
	 * simple array. Subclasses can implement mutation operations which return
	 * deep copies in which the argument values have been modified using
	 * {@link #_set(int, BOp)}.
	 * <p>
	 * If we allowed mutation of the arguments (outside of the object creation
	 * pattern) then caching of the arguments (or annotations) by classes such
	 * as EQ will cause {@link #clone()} to fail because (a) it will do
	 * a field-by-field copy on the concrete implementation class; and (b) it
	 * will not consistently update the cached references. In order to "fix"
	 * this problem, any classes which cache arguments or annotations would have
	 * to explicitly overrides {@link #clone()} in order to set those fields
	 * based on the arguments on the cloned {@link BOpBase} class.
	 * <p>
	 * Note: This must be at least "effectively" final per the effectively
	 * immutable contract for {@link BOp}s.
	 */
    private final BOp[] args;

    /**
     * The operator annotations.
     * <p>
     * Note: This must be at least "effectively" final per the effectively
     * immutable contract for {@link BOp}s.
     */
    private final Map<String,Object> annotations;

    /**
     * Deep copy constructor (required).
     * <p>
     * Each {@link BOp} MUST implement a public copy constructor with the
     * signature:
     * 
     * <pre>
     * public Foo(Foo)
     * </pre>
     * 
     * This construct is invoked by {@link #clone()} using reflection and is
     * responsible for the deep copy semantics for the {@link BOp}.
     * <p>
     * The default implementation makes a deep copy of {@link #args()} and
     * {@link #annotations()} but DOES NOT perform field-by-field copying.
     * Subclasses may simply delegate the constructor to their super class
     * unless they have additional fields which need to be copied.
     * <p>
     * This design pattern was selected because it preserves the immutable
     * contract of the {@link BOp} which gives us our thread safety and
     * visibility guarantees. Since the deep copy is realized by the {@link BOp}
     * implementation classes, it is important that each class take
     * responsibility for the deep copy semantics of any fields it may declare.
     * 
     * @param op
     *            A deep copy will be made of this {@link BOp}.
     * 
     * @throws NullPointerException
     *             if the argument is <code>null</code>.
     */
    public BOpBase(final BOpBase op) {
        // Note: only shallow copy is required to achieve immutable semantics!
		if (op.args == BOp.NOARGS || op.args.length == 0) {
			// fast path for zero arity operators.
			args = BOp.NOARGS;
        } else {
        	args = Arrays.copyOf(op.args, op.args.length);
        }
        annotations = new LinkedHashMap<String, Object>(op.annotations);
    }

    /**
     * Shallow copy constructor (required).
     * 
     * @param args
     *            The arguments to the operator.
     * @param annotations
     *            The annotations for the operator (optional).
     */
    public BOpBase(final BOp[] args,
            final Map<String, Object> annotations) {

        if (args == null)
            throw new IllegalArgumentException();
        
        checkArgs(args);
        
        this.args = args;

		this.annotations = (annotations == null ? new LinkedHashMap<String, Object>(
				DEFAULT_INITIAL_CAPACITY)
				: annotations);

    }

    @Override
    final public Map<String, Object> annotations() {

        return Collections.unmodifiableMap(annotations);
    
    }

    @Override
    protected boolean annotationsEqual(final BOp o) {

        if (o instanceof BOpBase) {

            // Fast path when comparing two immutable bops.
            return annotationsEqual(annotations, ((BOpBase) o).annotations);

        }

        return super.annotationsEqual(annotations, o.annotations());

    }
    
    /**
     * A copy of the args[] array.
     */
    final protected BOp[] argsCopy() {
        
        final BOp[] tmp = new BOp[args.length];

        for (int i = 0; i < args.length; i++) {
        
            tmp[i] = args[i];
            
        }

        return tmp;
        
    }

    /**
     * A copy of the annotations.
     */
    final protected Map<String, Object> annotationsCopy() {
        
        return new LinkedHashMap<String, Object>(annotations);

    }

    /**
     * A reference to the actual annotations map object. This is used in some
     * hot spots to avoid creating a new annotations map when we know that the
     * annotations will not be modified (annotations are always set within the
     * context in which the {@link BOpBase} instance is created so we can know
     * this locally by inspection of the code).
     */
    final protected Map<String,Object> annotationsRef() {
        
        return annotations;
        
    }
    
    @Override
    public BOp get(final int index) {
        
        return args[index];
        
    }
    
    /**
     * Set the value of an operand.
     * <p>
     * Note: This is protected to facilitate copy-on-write patterns. It is not
     * public to prevent arbitrary changes to operators outside of methods which
     * clone the operator and return the modified version. This is part of the
     * effectively immutable contract for {@link BOp}s.
     * 
     * @param index
     *            The index.
     * @param op
     *            The operand.
     *            
     * @return The old value.
     */
    final protected void _set(final int index, final BOp op) {
        
        this.args[index] = op;
        
    }

    /**
     * Return a new {@link BOpBase} in which the child operand has been replaced
     * by the given expression.
     * 
     * @param index
     *            The index of the child expression to be replaced.
     * @param newArg
     *            The new child expression.
     * 
     * @return A copy of this {@link BOpBase} in which the child operand has
     *         been replaced.
     */
    public BOpBase setArg(final int index, final BOp newArg) {

        if (newArg == null)
            throw new IllegalArgumentException();

        final BOpBase tmp = (BOpBase) this.clone();

        tmp._set(index, newArg);

        return tmp;

    }

    /**
     * Effectively overwrites the specified argument with the provided value.<p>
     * WARNING: this method could break logic of the code, which relies on immutability of the arguments list.
     * It is introduced while fixing issues with deferred IV resolution and intended to be used only before IV resolution completed.
     * This method triggers mutation notification.
     * @see https://jira.blazegraph.com/browse/BLZG-1755 (Date literals in complex FILTER not properly resolved)
     * @param index
     *            The index of the child expression to be replaced.
     * @param newArg
     *            The new child expression.
     */
    public void __replaceArg(final int index, final BOp newArg) {
    	args[index] = newArg;
    	mutation();
    }
    
    @Override
    public int arity() {
        
        return args.length;
        
    }

    /**
     * {@inheritDoc}
     * <p>
     * Note: This is much less efficient than {@link #argIterator()}.
     */
    @Override
    final public List<BOp> args() {

        return Collections.unmodifiableList(Arrays.asList(args));
        
    }

    /**
     * {@inheritDoc}
     * <p>
     * The iterator does not support removal. (This is more efficient than
     * #args()).
     */
    @Override
    final public Iterator<BOp> argIterator() {
    	
    	return new ArgIterator();
    	
    }

	/**
	 * An iterator visiting the arguments which does not support removal.
	 */
	private class ArgIterator implements Iterator<BOp> {

		private int i = 0;

		public boolean hasNext() {
			return i < args.length;
		}

		public BOp next() {
			if (!hasNext())
				throw new NoSuchElementException();
			return args[i++];
		}

		public void remove() {
			throw new UnsupportedOperationException();
		}

	}

    // shallow copy
    @Override
    public BOp[] toArray() {

        final BOp[] a = new BOp[args.length];

        return Arrays.copyOf(args, args.length, a.getClass());

    }

    // shallow copy
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(final T[] a) {
        if (a.length < args.length)
            return (T[]) Arrays.copyOf(args, args.length, a.getClass());
        System.arraycopy(args, 0, a, 0, args.length);
        if (a.length > args.length)
            a[args.length] = null;
        return a;
    }

//    /**
//     * Deep copy of a {@link BOpBase}.
//     * 
//     * @return The deep copy.
//     */
//    public BOpBase deepCopy() {
//        
//        final BOpBase bop = (BOpBase) this.clone();
//
//        bop.args = deepCopy(bop.args);
//        
//        bop.annotations = deepCopy(bop.annotations);
//        
//        return bop;
//        
//    }
    
    /**
     * Deep copy the arguments.
     * <p>
     * Note: As long as we stick to the immutable semantics for bops, we can
     * just make a shallow copy of the arguments in the "copy" constructor and
     * then modify them within the specific operator constructor before
     * returning control to the caller. This would result in less heap churn.
     */
    static protected BOp[] deepCopy(final BOp[] a) {
		if (a == BOp.NOARGS || a.length == 0) {
			// fast path for zero arity operators.
			return BOp.NOARGS;
        }
        final BOp[] t = new BOp[a.length];
        for (int i = 0; i < a.length; i++) {
            t[i] = a[i] == null ? null : a[i].clone();
        }
        return t;
    }

    /**
     * Deep copy the annotations.
     * <p>
     * Note: This does not know how to deep copy annotations which are not
     * {@link BOp}s or immutable objects such as {@link String}s or
     * {@link Number}s. Such objects should not be used as annotations.
     * 
     * @todo When attaching large data sets to a query plan they should be
     *       attached using a light weight reference object which allows them to
     *       be demanded by a node so deep copy remains a light weight
     *       operation. This also has the advantage that the objects are
     *       materialized on a node only when they are needed, which keeps the
     *       query plan small. Examples would be sending a temporary graph
     *       containing an ontology or some conditional assertions with a query
     *       plan.
     */
    static protected Map<String, Object> deepCopy(final Map<String, Object> a) {
        if (a == BOp.NOANNS) {
            // Fast past for immutable, empty annotations.
            return a;
        }
        // allocate map.
        final Map<String, Object> t = new LinkedHashMap<String, Object>(a
                .size());
        // copy map's entries.
        final Iterator<Map.Entry<String, Object>> itr = a.entrySet().iterator();
        while (itr.hasNext()) {
            final Map.Entry<String, Object> e = itr.next();
            if (e.getValue() instanceof BOp) {
                // deep copy bop annotations.
                t.put(e.getKey(), ((BOp) e.getValue()).clone());
            } else {
                // shallow copy anything else.
                t.put(e.getKey(), e.getValue());
            }
        }
        // return the copy.
        return t;
    }

//    @SuppressWarnings("unchecked")
//    public <T> T getProperty(final String name, final T defaultValue) {
//
//        if (!annotations.containsKey(name))
//            return defaultValue;
//
//        final Object val = annotations.get(name);
//
//        if (defaultValue != null && val.getClass() != defaultValue.getClass()) {
//
//            /*
//             * Attempt to convert to the correct target type.
//             */
//            
//            if (defaultValue.getClass() == Integer.class) {
//                return (T) Integer.valueOf("" + val);
//            }
//            if (defaultValue.getClass() == Long.class) {
//                return (T) Long.valueOf("" + val);
//            }
//            if (defaultValue.getClass() == Float.class) {
//                return (T) Float.valueOf("" + val);
//            }
//            if (defaultValue.getClass() == Double.class) {
//                return (T) Double.valueOf("" + val);
//            }
//
//        }
//
//        return (T) val;
//
//    }

//    @SuppressWarnings("unchecked")
//    public <T> T getProperty(final String name) {
//
//        return (T) annotations.get(name);
//
//    }

    @Override
    public Object getProperty(final String name) {

        return annotations.get(name);

    }

//    public <T> T getRequiredProperty(final String name) {
//
//        @SuppressWarnings("unchecked")
//        final T tmp = (T) annotations.get(name);
//
//        if (tmp == null)
//            throw new IllegalArgumentException("Required property: " + name);
//
//        return tmp;
//        
//    }

//    public Object getRequiredProperty(final String name) {
//
//        final Object tmp = annotations.get(name);
//
//        if (tmp == null)
//			throw new IllegalStateException("Required property: " + name
//					+ " : " + this);
//
//        return tmp;
//        
//    }

    /**
     * Set an annotation.
     * <p>
     * Note: This is protected to facilitate copy-on-write patterns. It is not
     * public to prevent arbitrary changes to operators outside of methods which
     * clone the operator and return the modified version. This is part of the
     * effectively immutable contract for {@link BOp}s.
     * 
     * @param name
     *            The name.
     * @param value
     *            The value.
     * 
     * @return The old value.
     */
    protected Object _setProperty(final String name, final Object value) {
        
        return annotations.put(name,value);
        
    }
    
    /**
     * Clear an annotation.
     * <p>
     * Note: This is protected to facilitate copy-on-write patterns. It is not
     * public to prevent arbitrary changes to operators outside of methods which
     * clone the operator and return the modified version. This is part of the
     * effectively immutable contract for {@link BOp}s.
     * 
     * @param name
     *            The name.
     */
    protected void _clearProperty(final String name) {
        
        annotations.remove(name);
        
    }

    @Override
    public BOpBase setProperty(final String name, final Object value) {

        final BOpBase tmp = (BOpBase) this.clone();

        tmp._setProperty(name, value);

        return tmp;

    }

    /**
     * Conditionally sets the property.
     * 
     * @param name
     *            The name.
     * @param value
     *            The value.
     * 
     * @return A copy of this {@link BOp} on which the property has been set.
     * 
     * @throws IllegalStateException
     *             if the property is already set.
     */
    public BOpBase setUnboundProperty(final String name, final Object value) {

        final BOpBase tmp = (BOpBase) this.clone();

        if (tmp._setProperty(name, value) != null)
            throw new IllegalStateException("Already set: name=" + name
                    + ", value=" + value);

        return tmp;

    }
    
    /**
     * Clear the named annotation.
     * 
     * @param name
     *            The annotation.
     *            
     * @return A copy of this {@link BOp} in which the named annotation has been
     *         removed.
     */
    public BOpBase clearProperty(final String name) {

        if (name == null)
            throw new IllegalArgumentException();

        final BOpBase tmp = (BOpBase) this.clone();

        tmp._clearProperty(name);

        return tmp;

    }

    /**
     * Strips off the named annotations.
     * 
     * @param names
     *            The annotations to be removed.
     * 
     * @return A copy of this {@link BOp} in which the specified annotations do
     *         not appear.
     */
    public BOp clearAnnotations(final String[] names) {

        final BOpBase tmp = (BOpBase) this.clone();

        for(String name : names) {
            
            tmp._clearProperty(name);
            
        }

        return tmp;
        
    }

}
